# # 代码3-1
# # 使用Conv2d构建卷积层
# import torch
# from torch import nn
#
# # 定义一个卷积层，输入通道数为1，输出通道数为3，卷积核大小为3*3，步长为1，填充为0
# layer = nn.Conv2d(1, 3, kernel_size=3, stride=1, padding=0)
# # 生成一个形状为(1,1,28,28)的随机张量x
# x = torch.rand(1, 1, 28, 28)
# # 用定义的卷积层对x进行前向传播，得到输出out
# out = layer.forward(x)
# # 打印输出out的形状
# print(out.shape)
#
# # 定义一个卷积层，输入通道数为1，输出通道数为3，卷积核大小为3*3，步长为1，填充为1
# layer = nn.Conv2d(1, 3, kernel_size=3, stride=1, padding=1)
# # 用定义的卷积层对x进行前向传播，并打印输出的形状
# print(layer.forward(x).shape)
#
# # 定义一个卷积层，输入通道数为1，输出通道数为3，卷积核大小为3*3，步长为2，填充为1
# layer = nn.Conv2d(1, 3, kernel_size=3, stride=2, padding=1)
# # 用定义的卷积层对x进行前向传播，并打印输出的形状
# print(layer.forward(x).shape)
#
# # 用定义的卷积层对x进行前向传播，得到输出out
# out = layer(x)
# # 打印输出out的形状
# print(out.shape)


# 理解torch.rand(1, 1, 3, 3)
# k = torch.rand(1, 1, 3, 3)
# print(k.shape)
# print(k)


# # 代码3-2
# # 使用ConvTranspose2d构建卷积层
# import torch
# from torch import nn
# x = torch.randn(1, 1, 2, 2)
# print(x.shape)
# # 导入nn模块的ConvTranspose2d类，该类用于实现二维上采样卷积操作
# l = nn.ConvTranspose2d(1, 1, 3)
# # 使用定义的ConvTranspose2d实例l对输入x进行操作，得到输出y
# y = l(x)
# print(y.shape)
#
#
#
# # 代码3-3
# # 使用Conv3d构建卷积层
# from torch import autograd
# m = nn.Conv3d(3, 3, (3, 7, 7), stride=1, padding=0)
# # 创建一个形状为(1, 3, 7, 60, 40)的随机张量，并将其转换为Variable类型
# input = torch.autograd.Variable(torch.randn(1, 3, 7, 60, 40))
# # 将input作为输入，通过模型m进行处理，得到输出结果
# output = m(input)
# print(output.size())

# # 代码3-4
# # 使用MaxPooling2d类构建池化层
# import torch
# m = torch.nn.MaxPool2d(3, stride=2)
# input = torch.autograd.Variable(torch.randn(20, 16, 50, 50))
# print(input.shape)
# output = m(input)
# print(output.shape)
# # 输出结果：
# # torch.Size([20, 16, 50, 50])
# # torch.Size([20, 16, 24, 24])
# m = torch.nn.MaxPool2d((2, 4), stride=2)
# input = torch.autograd.Variable(torch.randn(20, 16, 50, 50))
# print(input.shape)
# output = m(input)
# print(output.shape)
# # 输出结果：
# # torch.Size([20, 16, 50, 50])
# # torch.Size([20, 16, 25, 24])
# print("-----------")
#
# # 代码3-5
# # 使用AvgPool2d类构建池化层
# m = torch.nn. AvgPool2d(3, stride=2)
# input = torch. autograd.Variable(torch.randn(20, 16, 50, 50))
# print(input.shape)
# output = m(input)
# print(output.shape)
# # 输出结果：
# # torch.Size([20, 16, 50, 50])
# # torch.Size([20, 16, 24, 24])
# m = torch.nn.AvgPool2d((2, 4), stride=2)
# input = torch.autograd.Variable(torch.randn(20, 16, 50, 50))
# print(input.shape)
# output = m(input)
# print(output.shape)
# # 输出结果：
# # torch.Size([20, 16, 50, 50])
# # torch.Size([20, 16, 25, 24])
# print("-----------")


# # 代码3-6
# # 使用Linear类构建全连接层
# import torch
# from torch import nn
# connected_layer = nn.Linear(in_features=64 * 64 * 3, out_features=1)
# input = torch.randn(1, 64, 64, 3)
# # 通过view方法展平后变为(1, 12288)
# input = input.view(1, 64 * 64 * 3)
# print(input.shape)
# output = connected_layer(input)
# print(output.shape)
# print("-----------")
# # 输出结果：
# # torch.Size([1, 12288])
# # torch.Size([1, 1])
#
# # 代码3-7
# # 使用BatchNorm2d类构建归一化层
# m = nn.BatchNorm2d(2, affine=True)
# print(m.weight)
# print(m.bias)
# # 输出结果：
# # Parameter containing:
# # tensor([1., 1.], requires_grad=True)
# # Parameter containing:
# # tensor([0., 0.], requires_grad=True)
# input = torch.randn(1, 2, 3, 4)
# print(input)
# output = m(input)
# print(output)
# print(output.size())
# print("-----------")
#
# # 代码3-8
# # 使用Dropout类构建丢弃层
# import torch.autograd as autograd
#
# m = nn.Dropout(p=0.5)
# input = autograd.Variable(torch.randn(2, 6, 3))
# print(m(input))
# print("-----------")


# 代码3-9
# 导入相关库
import torch
# autograd自动微分
from torch.autograd import Variable
# 用于计算机视觉任务
import torchvision
# 用于数据加载
from torch.utils.data import DataLoader

# 代码3-10
# 超参数设置
n_epochs = 3  # 定义训练周期数，这里设为3
batch_size_train = 64  # 定义训练批次大小，这里设为64
batch_size_test = 1000  # 定义测试批次大小，这里设为1000
learning_rate = 0.01  # 定义学习率，这里设为0.01
momentum = 0.5  # 定义动量，这里设为0.5
log_interval = 10  # 定义日志记录间隔，这里设为10
random_seed = 1  # 定义随机种子，这里设为1
torch.manual_seed(random_seed)  # 使用torch库的manual_seed函数设置随机种子，保证实验可复现


# 代码3-11
# 加载数据集
train_loader = torch.utils.data.DataLoader(
  torchvision.datasets.MNIST('data3/',
                             train=True,    # 表示是训练集
                             download=False,     # 表示不进行下载操作（因为已经存在）
                             transform=torchvision.transforms.Compose([
                                 torchvision.transforms.ToTensor(),
                                 torchvision.transforms.Normalize((0.1307,), (0.3081,)) ])),
  batch_size=batch_size_train, shuffle=True)    # shuffle是指是否对取出的图片顺序进行打乱操作，以增加随机性从而提高模型泛化能力防止过拟合现象

test_loader = torch.utils.data.DataLoader(
  torchvision.datasets.MNIST('data3/',
                             train=False,
                             download=False,
                             transform=torchvision.transforms.Compose([
                                 torchvision.transforms.ToTensor(),
                                 torchvision.transforms.Normalize((0.1307,), (0.3081,)) ])),
  batch_size=batch_size_test, shuffle=True)

# 代码3-12
# 观察数据形状
examples = enumerate(test_loader)
batch_idx, (example_data, example_targets) = next(examples)
print(example_targets)
print(example_data.shape)
print(example_data)


# 代码3-13
# 绘制图片
# 导入 matplotlib.pyplot 模块，用于绘图
import matplotlib.pyplot as plt
# 设置全局字体为黑体（Heiti TC），确保中文字符能够正确显示
plt.rcParams['font.family'] = 'Heiti TC'
# 如果需要设置多个备选字体，可以将其设置为列表
# plt.rcParams['font.family'] = ['SimHei', 'KaiTi', 'FangSong', 'Microsoft YaHei']
# 创建一个新的图形对象
fig = plt.figure()
# 循环遍历6个数据样本
for i in range(6):
    # 在2行3列的网格中创建一个子图，位置为第i+1个子图
    plt.subplot(2, 3, i + 1)
    # 自动调整子图参数，使之填充整个图像区域
    plt.tight_layout()
    # 显示图像，example_data[i][0]表示第i个样本的第一个通道的数据（其实就是拿到[28, 28]）
    # cmap='gray'表示以灰度图显示，interpolation='none'表示不进行插值处理
    plt.imshow(example_data[i][0], cmap='gray', interpolation='none')
    # 设置子图标题，显示目标数据标签
    plt.title('目标数据标签: {}'.format(example_targets[i]))
    # 隐藏x轴刻度
    plt.xticks([])
    # 隐藏y轴刻度
    plt.yticks([])
# 显示所有子图
plt.show()


